package org.cloudplatform.common.feign.ribbon;

import feign.Client;
import feign.Request;
import feign.Response;
import org.cloudplatform.common.feign.model.Instance;
import org.cloudplatform.common.feign.register.InstanceRegister;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 类描述：
 * 作者：徐卫超 (cc)
 * 时间 2022/5/16 15:17
 */
public class MyRibbonClient implements Client {

    private final SSLSocketFactory sslContextFactory;
    private final HostnameVerifier hostnameVerifier;
    private final boolean disableRequestBuffering;
    private final InstanceRegister instanceRegister;
    private final AtomicInteger num = new AtomicInteger();




    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        // 获取路由信息
        URI asUri = URI.create(request.url());
        String clientName = asUri.getHost();
        String targetUri = getInstance(clientName).getUri().toString() + asUri.getPath();
        System.out.println(targetUri);
        HttpURLConnection connection = this.convertAndSend(request, targetUri, options);
        return this.convertResponse(connection, request);
    }

    public Instance getInstance(String serviceName) {
        List<Instance> instances = this.instanceRegister.serviceInstance(serviceName);
        if (instances.isEmpty()) {
            throw new RuntimeException("无可用服务");
        }
        return instances.get(num.incrementAndGet() % instances.size());
    }


    public MyRibbonClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier, InstanceRegister instanceRegister) {
        this(sslContextFactory, hostnameVerifier, instanceRegister, true);
    }

    static URI cleanUrl(String originalUrl, String host, String targetHost) {
        return URI.create(originalUrl.replaceFirst(host, targetHost));
    }

    public MyRibbonClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier,
                          InstanceRegister instanceRegister, boolean disableRequestBuffering) {
        this.sslContextFactory = sslContextFactory;
        this.hostnameVerifier = hostnameVerifier;
        this.disableRequestBuffering = disableRequestBuffering;
        this.instanceRegister = instanceRegister;
    }

    Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
        int status = connection.getResponseCode();
        String reason = connection.getResponseMessage();
        if (status < 0) {
            throw new IOException(String.format("Invalid status(%s) executing %s %s", status, connection.getRequestMethod(), connection.getURL()));
        } else {
            Map<String, Collection<String>> headers = new LinkedHashMap();
            Iterator var6 = connection.getHeaderFields().entrySet().iterator();

            while (var6.hasNext()) {
                Map.Entry<String, List<String>> field = (Map.Entry) var6.next();
                if (field.getKey() != null) {
                    headers.put(field.getKey(), field.getValue());
                }
            }

            Integer length = connection.getContentLength();
            if (length == -1) {
                length = null;
            }

            InputStream stream;
            if (status >= 400) {
                stream = connection.getErrorStream();
            } else {
                stream = connection.getInputStream();
            }
            return Response.builder().status(status).reason(reason).headers(headers).request(request).body(stream, length).build();
        }
    }

    public HttpURLConnection getConnection(URL url) throws IOException {
        return (HttpURLConnection) url.openConnection();
    }

    HttpURLConnection convertAndSend(Request request, String targetUrl, Request.Options options) throws IOException {
        URL url = new URL(targetUrl);
        HttpURLConnection connection = this.getConnection(url);
        if (connection instanceof HttpsURLConnection) {
            HttpsURLConnection sslCon = (HttpsURLConnection) connection;
            if (this.sslContextFactory != null) {
                sslCon.setSSLSocketFactory(this.sslContextFactory);
            }

            if (this.hostnameVerifier != null) {
                sslCon.setHostnameVerifier(this.hostnameVerifier);
            }
        }
        connection.setConnectTimeout(options.connectTimeoutMillis());
        connection.setReadTimeout(options.readTimeoutMillis());
        connection.setAllowUserInteraction(false);
        connection.setInstanceFollowRedirects(options.isFollowRedirects());
        connection.setRequestMethod(request.httpMethod().name());
        Collection<String> contentEncodingValues = (Collection) request.headers().get("Content-Encoding");
        boolean gzipEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains("gzip");
        boolean deflateEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains("deflate");
        boolean hasAcceptHeader = false;
        Integer contentLength = null;
        Iterator var10 = request.headers().keySet().iterator();

        while (var10.hasNext()) {
            String field = (String) var10.next();
            if (field.equalsIgnoreCase("Accept")) {
                hasAcceptHeader = true;
            }

            Iterator var12 = ((Collection) request.headers().get(field)).iterator();

            while (var12.hasNext()) {
                String value = (String) var12.next();
                if (field.equals("Content-Length")) {
                    if (!gzipEncodedRequest && !deflateEncodedRequest) {
                        contentLength = Integer.valueOf(value);
                        connection.addRequestProperty(field, value);
                    }
                } else {
                    connection.addRequestProperty(field, value);
                }
            }
        }

        if (!hasAcceptHeader) {
            connection.addRequestProperty("Accept", "*/*");
        }

        if (request.body() != null) {
            if (this.disableRequestBuffering) {
                if (contentLength != null) {
                    connection.setFixedLengthStreamingMode(contentLength);
                } else {
                    connection.setChunkedStreamingMode(8196);
                }
            }

            connection.setDoOutput(true);
            OutputStream out = connection.getOutputStream();
            if (gzipEncodedRequest) {
                out = new GZIPOutputStream((OutputStream) out);
            } else if (deflateEncodedRequest) {
                out = new DeflaterOutputStream((OutputStream) out);
            }

            try {
                ((OutputStream) out).write(request.body());
            } finally {
                try {
                    ((OutputStream) out).close();
                } catch (IOException var19) {
                }

            }
        }

        return connection;
    }


}
